﻿#! python
#! coding: utf-8

import os
import json
import urllib2
import wx
import re
import itertools
import cProfile
import pstats
import StringIO
import datetime


def load_file(filename, default=None):
    data = default or {}
    if os.path.exists(filename):
        with open(filename, 'r') as f:
            new_settings = json.load(f)
            data.update(new_settings)
    return data


def save_file(filename, data):
    with open(filename, 'w') as f:
        json.dump(data, f, indent=2)


def calc_width(rels, height, border):
    """
    Рассчитать общую ширину полосы фотографий при данных высоте
    и границах (справа и слева) рамки
    @param rels: набор отношений ширины к высоты для каждой картинки
    @param height: высота полосы
    @param border: ширина рамки для каждой картинки
    @return: рассчитанная ширина полосы
    """
    return sum([height * r + 2 * border for r in rels])


def calc_height(rels, width, border):
    """
    Рассчитать оптимальную высоту для полосы фотографий так,
    чтобы все фотографии влезли в полосу, сохранив пропорции
    каждой фотографии.

    Использует метод бинарного поиска.

    Огромное спасибо Данилову Александру и Бутину Кириллу
    за подсказку алгоритма нахождения оптимальной высоты

    @param rels: набор отношений ширины к высоты для каждой картинки
    @param width: ширина полосы
    @param border: ширина рамки для каждой картинки
    @return: оптимальная высота полосы
    """
    low = 0.0
    high = 65536.0  # max resolution
    while high - low > 0.1:
        middle = (high + low) / 2
        if calc_width(rels, middle, border) < width:
            low = middle
        else:
            high = middle
    return (high + low) / 2


def calc_frames(rels, stripe_width, stripe_border, stripe_height):
    """
    На основе переданных отношений изображений вычисляет оптимальные
    параметры ширины и высоты, чтобы разместить их в полосу.
    """
    do_check = True

    # вычисляем высоту для полосы
    # у всех изображений высота будет одинаковая
    height = int(round(calc_height(rels, stripe_width, stripe_border)))

    # если полученая высота больше максимально допустимой,
    # то ее придется уменьшить (от этого уменьшится и ширина полосы)
    if height >= stripe_height:
        height = stripe_height
        do_check = False

    # вычисляем разрешения изображений
    dims = [(int(round(height * r)), height) for r in rels]

    # выполняем проверку
    # иногда при расчетах новой ширины для изображений, их суммарная ширина
    # может на несколько точек отличатсья от ширины полосы;
    # поэтому корректируем последнее изображение в полосе,
    # растягивая или сжимая его
    if do_check:
        # контрольная сумма
        check_width = sum([w + 2 * stripe_border for w, h in dims])
        # разница ширины в точках
        delta = stripe_width - check_width
        # получаем размеры последнего изображения
        w, h = dims[-1]
        # изменяем размеры последнего изображения
        dims[-1] = w + delta, h

    return dims


def render_template(template, remote_photo, w, h):
    """
    Заполнить указанный шаблон. Плейсхолдеры в шаблоне имеют следующий формат: {{<размер>:<атрибут>}},
    где размер: число, тогда будет найдено изображение не меньше квадрата с такой сороной
                * - будет найдено изображение, подходящее под параметры w, h, которые в свою очередь подобраны
                алгоритмом по выстраиванию изображений calc_frames
        атрибут: один из атрибутов YandexImage w, h, href
    @type template: string|unicode
    @type remote_photo: photo.RemotePhoto
    @param w:
    @param h:
    @return:
    """
    def repl(match):
        name, attr = match.group(1).split(':')[:2]
        adjusment = name == '*'
        if adjusment:
            img = remote_photo.nearest(w, h)
        else:
            size = int(name)
            img = remote_photo.nearest(size, size)
        if attr == 'w':
            return str(w if adjusment else img.w)
        if attr == 'h':
            return str(h if adjusment else img.h)
        if attr == 'href':
            return str(img.href)

    return re.sub('{{(.*?)}}', repl, template, re.UNICODE)


def text_to_clipboard(text):
    """
    Помещает переданный текст в буфер обмена
    @param text: текст
    @return: удалось или нет
    """
    do = wx.TextDataObject()
    do.SetText(text)
    result = wx.TheClipboard.Open()
    if result:
        wx.TheClipboard.SetData(do)
        wx.TheClipboard.Close()
    return result


def fit(imgW, imgH, frameW, frameH):
    """
    Уместить прямоугольник со сторонами imgW и imgH
    в прямоугольник со сторонами frameW, frameH, сохраняя пропорции
    исходного прямоугольника.
    @param imgW: ширина прямоугольника
    @param imgH: высота прямоугольника
    @param frameW: ширина рамки
    @param frameH: высота рамки
    @return: размер вписанного прямоугольника
    """
    if imgW == 0 and imgH == 0:
        return 0, 0
    scale = min(1.0, float(frameW) / imgW, float(frameH) / imgH)
    return imgW * scale, imgH * scale


def resize(wximage, w, h, quality=wx.IMAGE_QUALITY_HIGH):
    """
    Изменить размер изображения таким образом, чтобы оно вписывалось
    в жанный прямоугльник со сторонами w, h
    @param wximage: изображение
    @param w: ширина прямоугольника
    @param h: высота прямоугольника
    @param quality: качество конвертации
    @return: измененное изображение
    """
    img_w = wximage.GetWidth()
    img_h = wximage.GetHeight()
    new_w, new_h = fit(img_w, img_h, w, h)
    return wximage.Rescale(new_w, new_h, quality)


def find_files(path, pattern):
    regex = re.compile(pattern, re.IGNORECASE | re.UNICODE)
    for dirpath, dirnames, filenames in os.walk(path):
        for f in filenames:
            if re.match(regex, f):
                yield os.path.join(dirpath, f)


def get_mtime(path, pattern):
    regex = re.compile(pattern, re.IGNORECASE | re.UNICODE)
    mtime = os.stat(path).st_mtime
    for dirpath, dirnames, filenames in os.walk(path):
        has_files = any((re.match(regex, f) for f in filenames))
        if has_files:
            mtime = max(mtime, os.stat(dirpath).st_mtime)
    return datetime.datetime.fromtimestamp(mtime)


def check_files(path, pattern):
    return bool(list(itertools.islice(find_files(path, pattern), 1)))


def count_files(path, pattern):
    return sum(1 for i in find_files(path, pattern))


def http_handler(f):
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except urllib2.HTTPError as err:
            msg = str(err)
            if err.code == 403:
                msg += u'\nНе удается войти в систему. Проверьте правильность имени пользователя и пароля.'
            wx.MessageBox(msg, u'Ошибка', style=wx.ICON_ERROR)
    return wrapper


def long_time(f):
    def wrapper(*args, **kwargs):
        wx.BeginBusyCursor()
        try:
            return f(*args, **kwargs)
        finally:
            wx.EndBusyCursor()
    return wrapper


def profile(f):
    def wrapper(*args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()
        # начало функции
        result = f(*args, **kwargs)
        # конец функции
        pr.disable()
        s = StringIO.StringIO()
        sortby = 'cumulative'
        ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
        ps.print_stats()
        print s.getvalue()
        return result
    return wrapper

#-------------------------------------------------------------------------------


def main():
    import itertools
    print list(itertools.islice(find_files(u'M:\\', '.+_lg\.jpg'), 1))
    # for f in :
    #     print f

if __name__ == '__main__':
    main()
